#ifndef MESH_RENDERER_H 
#define MESH_RENDERER_H

#include <string>
#include <memory>
#include <vector>

#include "../geom/PolyMesh.h"
#include "../util/NTuple.h"
#include "GLInstanceInfo.h"
#include "FreeCamera.h"

using namespace std;

namespace glutil
{
	/** This class renders a set of instances of a certain mesh in a 3D world */
	class MeshRenderer
	{
		protected:
			/** Mesh to render */
			const geom::PolyMesh * m_mesh;
			/** Per-instance information */
			std::vector<GLInstanceInfo> m_instances;
			/** Bounding sphere for this mesh. Used for frustum culling */
			geom::BSphere m_meshBSphere;

			/** Vertices VBO ID */
			unsigned int m_vbufferID; 
			/** Indices VBO ID */
			unsigned int m_ibufferID; 
			/** Normals VBO ID */
			unsigned int m_nbufferID;
			/** Colors VBO ID */
			unsigned int m_cbufferID;
			/** Total number of tris in an instance */
			size_t m_numberOfTris;
			/** Biggest index among the points */
			int maxPointIndex;
			/** Sets this objects VBOs as the current buffers in OpenGL */
			void setGLRenderState(bool withMaterials);
			/** Unsets the current buffers in OpenGL */
			void unsetGLRenderState();

		public:
			/** Constructs a renderer
			@param p The mesh this renderer will display */
			MeshRenderer(const geom::PolyMesh * p);
			
			/** Destructor */
			~MeshRenderer();

			/** Adds another instance to the renderer 
				@param m Instance to add to renderer */
			void addInstance(const GLInstanceInfo & m);

			/** Removes all instances from the renderer*/
			void clearInstances();

			/** Gets the rotation for a given instance
			@param instance Instance ID */
			const Triplet<float>& getRot(int instance) const;
			/** Gets the position of a given instance
			@param instance Instance ID */
			const Triplet<float>& getPos(int instance) const;

			/** Renders one of the instances in the object
			 @param withMaterials Option to render the mesh with, or without materials
			@param instance Instance to render*/
			void render(int instance, bool withMaterials);

			/** Renders all instances in the object 
			 @param withMaterials Option to render the mesh with, or without materials*/
			void renderAll(bool withMaterials);

			/** Renders all instances that fall inside the camera frustum and are closer than a certain distance
			 @param withMaterials Option to render the mesh with, or without materials
			 @param camera Current camera to get the frustum from
			 @param maxDistance Maximum distance to render
			 */
			void renderAll(bool withMaterials, const FreeCamera * camera, float maxDistance, int selectedTree, float baseScale=1.0f);
			/** Updates the VBOs to store the current mesh data */
			void generateVertexBufferObjects();

		private:
			MeshRenderer();
	};
}

#endif
